/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <unistd.h>
//#include "errno.h"
#include "cmsis_os2.h"
#include "sys/time.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"

#include "cJSON.h"
#include "ohos_types.h"
#include "iot_softap.h"
#include "iot_netcfg.h"
#include "iot_bes2600_debug.h"
#include "iot_demo_def.h"

#define SSID_MAX_LEN    32
#define PWD_MAX_LEN     32
#define NET_PORT        8686
#define RETRY_TIMES     5
#define CONNECT_TIMEOUT 20
#define CMD_KEY    "cmd"
#define PAR_KEY     "param"
#define SID_KEY     "wifiName"
#define PWD_KEY     "wifiPassword"
#define CMD_CONFIG  0x20
#define REQUEST_STR "{\"cmd\":32,\"code\":200,\"msg\":\"\"}"

static int ParseWifiDataInfo(const char *psr, char *key, char *buff, int pl)
{
    cJSON *json;
    cJSON *sub;
    cJSON *obj;

    if (psr == NULL || key == NULL || buff == NULL || pl <= 0) {
        LOG_E("NULL POINT! \n");
        return -1;
    }

    json = cJSON_Parse(psr);
    if (json == NULL) {
        LOG_E("parse_json: %s\n", psr);
        return -1;
    }

    sub = cJSON_GetObjectItem(json, PAR_KEY);
    if (sub == NULL) {
        LOG_E("cJSON_GetObjectItem(%s) failed\n", PAR_KEY);
        return -1;
    }
    obj = cJSON_GetObjectItem(sub, key);
    if (obj == NULL) {
        LOG_E("cJSON_GetObjectItem(%s) failed\n", key);
        return -1;
    }

    memset(buff, 0x00, pl);
    strcpy(buff, cJSON_GetStringValue(obj));

    cJSON_Delete(json);
    return 0;
}

static int ParseWifiInfo(const char *psr, char *ssid, int sl, char *pwd, int pl)
{
    cJSON *json;
    cJSON *sub;
    cJSON *obj;
    int cmd;
    char *ps = NULL;
    char *pw = NULL;

    json = cJSON_Parse(psr);
    LOG_E("parse_json: %s\n", psr);
    if (json == NULL) {
        LOG_E("parse_json: %s\n", psr);
        return -1;
    }

    sub = cJSON_GetObjectItem(json, CMD_KEY);
    cmd = cJSON_GetNumberValue(sub);
    if (cmd != CMD_CONFIG) {
        LOG_E("CMD TYPE FAILED! cmd=%d \n", cmd);
        cJSON_Delete(json);
        return -1;
    }

    sub = cJSON_GetObjectItem(json, PAR_KEY);
    if (sub == NULL) {
        LOG_E("get param obj failed! \n");
        cJSON_Delete(json);
        return -1;
    }

    obj = cJSON_GetObjectItem(sub, SID_KEY);
    ps = cJSON_GetStringValue(obj);
    if (ps == NULL) {
        LOG_E("get ssid failed! \n");
        cJSON_Delete(json);
        return -1;
    }

    if (strncpy_s(ssid, sl, ps, strlen(ps)) < 0) {
        LOG_E("strncpy_s failed! \n");
        cJSON_Delete(json);
        return -1;
    }

    obj = cJSON_GetObjectItem(sub, PWD_KEY);
    pw = cJSON_GetStringValue(obj);
    if (pw != NULL) {
        LOG_I("pw=%s\n", pw);
        if (strncpy_s(pwd, pl, pw, strlen(pw)) < 0) {
            LOG_E("strncpy_s failed! \n");
            cJSON_Delete(json);
            return -1;
        }
    }

    cJSON_Delete(json);
    return 0;
}

static int NetCfgGetSocketValue(int sockfd, IOT_WifiInfo *info, DataInfo *data)
{
    int result = -1;

    if (sockfd < 0) {
        LOG_E("sockfd invalid!\n");
        return -1;
    }

    if (info == NULL) {
        LOG_E("NULL POINT!\n");
        return -1;
    }

    while (1) {
        int readbytes;
        struct sockaddr_in addr;
        char recvbuf[BUFF_SIZE] = {0};
        socklen_t len = sizeof(addr);

        readbytes = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&addr, &len);
        if (readbytes <= 0) {
            LOG_E("socket disconnect! \n");
            break;
        }

        if (ParseWifiInfo((const char *)recvbuf, info->ssid, sizeof(info->ssid), info->psk, sizeof(info->psk)) == 0) {
            if (data != NULL) {
                ParseWifiDataInfo((const char *)recvbuf, data->key, data->data, sizeof(data->data));
            }

            sendto(sockfd, REQUEST_STR, strlen(REQUEST_STR), 0, (struct sockaddr *)&addr, &len);
            result = 0;
            break;
        }
    }

    return result;
}

static int NetCfgGetWifiInfo(IOT_WifiInfo *info, DataInfo *data)
{
    int sockfd;
    struct sockaddr_in servaddr;
    if (info == NULL) {
        LOG_E("NULL POINT! \n");
        return -1;
    }

    if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        LOG_E("socket error! \n");
        return -1;
    }

    memset_s(&servaddr, sizeof(servaddr), 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(NET_PORT);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    LOG_I("listen port %d\n", NET_PORT);
    if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        LOG_E("bind socket! \n");
        close(sockfd);
        return -1;
    }

    if (listen(sockfd, 1) < 0) {
        LOG_E("listen failed! errno = %d \n", errno);
        close(sockfd);
        return -1;
    }

    while (1) {
        int newfd, retval;
        struct sockaddr_in client_addr;
        socklen_t length = sizeof(client_addr);
        newfd = accept(sockfd, (struct sockaddr *)&client_addr, &length);
        LOG_I("new socket connect!\n");
        retval = NetCfgGetSocketValue(newfd, info, data);
        close(newfd);

        if (retval == 0) {
            LOG_I("config network success! \n");
            break;
        }
    }

    close(sockfd);

    return 0;
}

int BOARD_NetCfgStartConfigWithData(const char *appName, IOT_WifiInfo *info, DataInfo *data)
{
    int retval = 0;

    if (BOARD_SoftApStart(appName) < 0) {
        LOG_E("start softap failed! \n");
        return -1;
    }

    if (NetCfgGetWifiInfo(info, data) < 0) {
        LOG_E("start softap failed! \n");
        retval = -1;
    }

    BOARD_SoftApStop();

    return retval;
}

int BOARD_NetCfgStartConfig(const char *appName, char *ssid, int sLen, char *pwd, int pLen)
{
    int retval;
    IOT_WifiInfo info;

    retval = BOARD_NetCfgStartConfigWithData(appName, &info, NULL);
    if (retval == 0) {
        strncpy(ssid, info.ssid, strlen(info.ssid));
        strncpy(pwd, info.psk, strlen(info.psk));
    }

    return retval;
}